use super::condvar::Condvar;
use crate::sync::arc::Arc;
use crate::{Error, Result};
use alloc::collections::LinkedList;
use core::time::Duration;

pub struct SyncSender<T: Send> {
    channel: Arc<Channel<T>>,
}

impl<T: Send> Clone for SyncSender<T> {
    fn clone(&self) -> Self {
        Self {
            channel: self.channel.clone(),
        }
    }
}

impl<T: Send> SyncSender<T> {
    pub fn send(&self, data: T) -> Result<()> {
        self.channel.send(data)
    }
}

pub struct Receiver<T: Send> {
    channel: Arc<Channel<T>>,
}

impl<T: Send> Receiver<T> {
    pub fn try_recv(&self) -> Result<T> {
        self.channel.recv(0)
    }

    pub fn recv(&self, to: u32) -> Result<T> {
        self.channel.recv(to)
    }
}

struct Channel<T: Send> {
    size: usize,
    queue: Condvar<LinkedList<T>>,
}

impl<T: Send> Channel<T> {
    pub fn new(limit: usize) -> Channel<T> {
        Self {
            size: limit,
            queue: Condvar::new(LinkedList::new()),
        }
    }

    pub fn send(&self, data: T) -> Result<()> {
        {
            let mut queue = self.queue.lock().unwrap();
            if queue.len() >= self.size {
                warnln!(
                    "current queue size {} limit {}, full, ignore new data",
                    queue.len(),
                    self.size
                );
                return Err(Error::Full);
            }
            queue.push_back(data);
        }
        self.queue.notify_all();
        Ok(())
    }

    fn recv(&self, timeout: u32) -> Result<T> {
        {
            let mut queue = self.queue.lock().unwrap();
            if let Some(data) = queue.pop_front() {
                return Ok(data);
            }
        }
        let mut queue = self
            .queue
            .wait_timeout(Duration::from_millis(timeout as u64))
            .unwrap();
        queue.pop_front().ok_or(Error::Timeout)
    }
}

impl<T: Send> Drop for Channel<T> {
    fn drop(&mut self) {}
}

pub fn sync_channel<T: Send>(size: usize) -> (SyncSender<T>, Receiver<T>) {
    let chn: Arc<Channel<T>> = Arc::new(Channel::new(size));
    (
        SyncSender {
            channel: chn.clone(),
        },
        Receiver {
            channel: chn.clone(),
        },
    )
}
